import csv
import json
from functools import partial
import os
from pathlib import Path

from PySide2.QtGui import QStandardItemModel, QStandardItem, QBrush, QColor, QKeySequence
from PySide2.QtWidgets import QWidget, QAction, QHeaderView, QApplication, QMessageBox, QMenu
from PySide2.QtCore import Qt, QFileSystemWatcher, QTimer

from .form.widget_thickness_ui import Ui_formThickness as FormThickness
from business.business1 import ExcelSheet


def str_to_float(s):
    try:
        return float(s)
    except (ValueError, TypeError):
        return 0.0


def create_folder_and_file(file_name):
    """创建文件夹和文件（如果不存在）"""
    file_path = Path(file_name)
    folder_path = file_path.parent

    if not folder_path.exists():
        os.makedirs(folder_path, exist_ok=True)

    # 创建文件（如果不存在）
    if not file_path.exists():
        file_path.touch()


class WidgetThickness(QWidget, FormThickness):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.file_csv_path = 'data/data.csv'
        self.file_json_path = 'data/data.json'

        # 如果没有文件那么先创建
        create_folder_and_file(self.file_csv_path)
        create_folder_and_file(self.file_json_path)

        self.vertical_scroll_pos = 0

        # 将CSV数据放到ExcelSheet
        settings = self.load_settings_and_init_line_edit()
        self.well_depth1 = settings.get('well_depth1', 0)
        self.well_depth2 = settings.get('well_depth2', 0)
        self.tilt_angle = settings.get('tilt_angle', 0)
        orientation = settings.get('orientation', 0)
        ground_x = settings.get('ground_x', 0)
        ground_y = settings.get('ground_y', 0)
        self.excel_sheet: ExcelSheet = ExcelSheet(orientation, ground_x, ground_y)
        self.csv_to_excel_sheet()

        # 展示表格
        self.table_headers = [
            ('米', '井深'),
            ('度', '井斜'),
            ('度', '方位'),
            ('米', '垂深'),
            ('米(N+/S-)', '南北坐标'),
            ('米(E+/W-)', '东西坐标'),
            ('度', '闭合方位'),
            ('米', '闭合位移'),
            ('米', '视平位移'),
            ('度/30米', '狗腿度'),
            ('米', '纵坐标(X)'),
            ('米', '纵坐标(Y)')
        ]
        self.table_view1_model = QStandardItemModel()
        self.table_view2_model = QStandardItemModel()
        self.init_table2()

        # 添加 Action
        self.action_copy = QAction('复制', self.tableView2)
        self.action_paste = QAction('粘贴', self.tableView2)
        self.action_delete = QAction('选中的内容置为0', self.tableView2)
        self.action_del_row = QAction('删除选中的行', self.tableView2)
        self.init_actions()

        # 添加上下文菜单
        self.tableView2.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tableView2.customContextMenuRequested.connect(self.show_context_menu)

        # 添加信号与槽
        self.table_view1_model.itemChanged.connect(self.slot_table_view1_model)
        self.table_view2_model.itemChanged.connect(self.slot_table_view2_model)
        self.lineEditOrientation.editingFinished.connect(self.slot_line_edit)
        self.lineEditX.editingFinished.connect(self.slot_line_edit)
        self.lineEditY.editingFinished.connect(self.slot_line_edit)
        self.lineEditTiltAngle.editingFinished.connect(self.slot_line_edit)
        self.comboBox.currentIndexChanged.connect(self.calc_true_thickness)

        # 消息弹窗
        self.msg_box = QMessageBox(self)
        self.msg_box.setIcon(QMessageBox.Warning)
        self.msg_box.setWindowTitle("警告")
        self.msg_box.setText("只能在前3列和第1行之后的范围粘贴！")
        self.msg_box.setStandardButtons(QMessageBox.Ok)

        # 监听文件改动并自动读取数据
        self.watcher_csv = QFileSystemWatcher()
        self.watcher_json = QFileSystemWatcher()
        self.init_file_watcher()

        # 启动时，计算真厚度
        self.calc_true_thickness(self.comboBox.currentIndex())

    def slot_table_view1_model(self, item: QStandardItem):
        row = item.row()
        col = item.column()
        target = str_to_float(item.text())
        if row == 0 and col == 0:
            self.well_depth1 = target
            self.init_table1()
        if row == 1 and col == 0:
            self.well_depth2 = target
            self.init_table1()
        self.save_settings()  # 保存配置

    def slot_table_view2_model(self, item: QStandardItem):
        row = item.row()
        col = item.column()
        value = str_to_float(item.text())

        if row <= 0 or col >= 3:
            return

        if not (col == 0 and value == 0.0):
            # 禁止第一列为0
            self.excel_sheet.safe_set_value(row - 1, col, value)

        self.init_table2()

    def slot_line_edit(self):
        orientation = str_to_float(self.lineEditOrientation.text())
        ground_x = str_to_float(self.lineEditX.text())
        ground_y = str_to_float(self.lineEditY.text())
        tilt_angle = str_to_float(self.lineEditTiltAngle.text())

        self.excel_sheet.orientation = orientation
        self.excel_sheet.ground_x = ground_x
        self.excel_sheet.ground_y = ground_y

        self.excel_sheet.base_row[10] = ground_x
        self.excel_sheet.base_row[11] = ground_y

        self.tilt_angle = tilt_angle

        # 更新输入框文本
        self.lineEditOrientation.setText(str(orientation))
        self.lineEditX.setText(str(ground_x))
        self.lineEditY.setText(str(ground_y))
        self.lineEditTiltAngle.setText(str(tilt_angle))

        # 保存数据
        self.save_settings()

        # 更新表格
        self.init_table2()

    def slot_copy_selection(self):
        selected_indexes = self.get_selected_indexes()
        if not selected_indexes:
            return

        # Get the range of selected rows and columns
        rows = {index.row() for index in selected_indexes}
        cols = {index.column() for index in selected_indexes}

        min_row, max_row = min(rows), max(rows)
        min_col, max_col = min(cols), max(cols)

        # Prepare the data for copying
        copied_lines = []
        for row in range(min_row, max_row + 1):
            row_items = []
            for col in range(min_col, max_col + 1):
                # Check if the cell is actually selected (for non-contiguous selections)
                if any(idx.row() == row and idx.column() == col for idx in selected_indexes):
                    item = self.table_view2_model.item(row, col)
                    value = item.data(Qt.DisplayRole) if item else ""
                    row_items.append(str(value) if value is not None else "")
                else:
                    row_items.append("")  # Fill empty for unselected cells in the range

            copied_lines.append("\t".join(row_items))

        # Copy to clipboard
        QApplication.clipboard().setText("\n".join(copied_lines))

    def slot_paste_selection(self):
        if not self.tableView2.hasFocus():
            return
        clipboard = QApplication.clipboard()
        clipboard_text = clipboard.text()
        if not clipboard_text:
            return

        # Get current selection position
        selection = self.tableView2.selectionModel()
        if not selection.hasSelection():
            return

        else:
            current_index = selection.selectedIndexes()[0]

        start_row = current_index.row()
        start_col = current_index.column()

        # Check if starting position is valid (row > 0 and column < 3)
        if start_row <= 0 or start_col >= 3:
            self.msg_box.show()
            return

        # Parse clipboard data
        rows = clipboard_text.split('\n')
        data = []
        for row in rows:
            if row:
                data.append(row.split('\t'))

        # Check if paste would go into invalid columns
        max_col = start_col + max(len(row) for row in data) - 1
        if max_col >= 3:
            self.msg_box.show()
            return

        # Paste data
        try:
            for row_offset, row_data in enumerate(data):
                target_row = start_row + row_offset
                # Skip if trying to paste into first row
                if target_row <= 0:
                    continue

                for col_offset, cell_data in enumerate(row_data):
                    target_col = start_col + col_offset
                    # Skip if trying to paste into columns beyond 2
                    if target_col >= 3:
                        continue

                    # 更新 ExcelSheet 对象中的数据
                    # 尝试转换为浮点数
                    try:
                        float_value = float(cell_data)
                        self.excel_sheet.safe_set_value(target_row - 1, target_col, float_value)
                    except ValueError:
                        self.excel_sheet.safe_set_value(target_row - 1, target_col, 0.0)
        except Exception as e:
            print(e)
        else:
            # 更新表格
            self.init_table2()

    def get_selected_indexes(self):
        if not self.tableView2.hasFocus():
            return None

        selection = self.tableView2.selectionModel()
        if not selection.hasSelection():
            return None

        return selection.selectedIndexes()

    def slot_delete_selection(self):
        selected_indexes = self.get_selected_indexes()
        if not selected_indexes:
            return

        # Get the range of selected rows and columns
        rows = {index.row() for index in selected_indexes}
        cols = {index.column() for index in selected_indexes}

        min_row, max_row = min(rows), max(rows)
        min_col, max_col = min(cols), max(cols)

        table_row_cnt = self.table_view2_model.rowCount()
        for row in range(min_row, max_row + 1):
            for col in range(min_col, max_col + 1):
                if 1 <= row < table_row_cnt - 1 and 1 <= col < 3:
                    # 对应的值改为 0，禁止第一列为0
                    self.excel_sheet.safe_set_value(row - 1, col, 0.0)
        self.init_table2()

    def slot_del_row_selection(self):
        selected_indexes = self.get_selected_indexes()
        if not selected_indexes:
            return
        rows = {index.row() for index in selected_indexes}
        min_row, max_row = min(rows), max(rows)
        if min_row >= 1:
            min_row, max_row = (min_row - 1, max_row)
        del self.excel_sheet.rows[min_row:max_row]
        self.init_table2()

    def csv_to_excel_sheet(self):
        """CSV 转 ExcelSheet"""
        with open(self.file_csv_path, mode='r', encoding='utf-8') as file:
            self.excel_sheet.rows.clear()
            csv_reader = csv.reader(file)
            for row in csv_reader:
                if len(row) >= 3:
                    # 直接self.excel_sheet.rows，避免 ExcelSheet.append_row() 每次计算表格数据
                    self.excel_sheet.rows.append([float(row[0]), float(row[1]), float(row[2])])

    def init_table1(self):
        # 设置表头样式
        self.tableView1.horizontalHeader().setStyleSheet("""
            QHeaderView::section {
                background-color: #007cb0;
                color: white;
                border: 1px solid #193153;
                font-weight: bold;
            }
        """)
        self.tableView1.verticalHeader().setStyleSheet("""
            QHeaderView::section {
                color: red;
                font-weight: bold;
            }
        """)
        self.tableView1.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.tableView1.verticalHeader().setFixedWidth(60)
        self.tableView1.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

        self.tableView1.setModel(self.table_view1_model)

        # 添加行号
        self.table_view1_model.setVerticalHeaderLabels(['特征1', '特征2'])

        # 添加表头
        for col, (unit, description) in enumerate(self.table_headers):
            header_item = QStandardItem(f'{unit}\n{description}')
            self.table_view1_model.setHorizontalHeaderItem(col, header_item)

        # 填充数据
        excel_row1 = self.excel_sheet.linear_interpolation(self.well_depth1)
        excel_row2 = self.excel_sheet.linear_interpolation(self.well_depth2)

        # 防止递归触发，函数末尾需要(False)
        self.table_view1_model.blockSignals(True)
        for row_idx, row_data in enumerate([excel_row1, excel_row2]):
            for col_idx in range(0, 12):
                item = QStandardItem()
                if len(row_data) < 12:
                    item.setData('', Qt.DisplayRole)
                else:
                    item.setData(format(row_data[col_idx], '.2f'), Qt.DisplayRole)
                    item.setToolTip(str(row_data[col_idx]))
                # 设置文本对齐方式为右对齐，更适合数字显示
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                if col_idx >= 1:
                    item.setEditable(False)
                    item.setBackground(QBrush(QColor(255, 255, 153)))
                else:
                    item.setEditable(True)
                self.table_view1_model.setItem(row_idx, col_idx, item)
        self.table_view1_model.blockSignals(False)

        self.calc_true_thickness(self.comboBox.currentIndex())

    def init_table2(self):
        self.vertical_scroll_pos = self.tableView2.verticalScrollBar().value()

        self.excel_sheet.procedure_excel_sheet()
        # 设置表头样式
        self.tableView2.horizontalHeader().setStyleSheet("""
            QHeaderView::section {
                background-color: #007cb0;
                color: white;
                border: 1px solid #193153;
                font-weight: bold;
            }
        """)
        self.tableView2.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.tableView2.verticalHeader().setFixedWidth(60)
        self.tableView2.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

        self.tableView2.setModel(self.table_view2_model)
        # 必须要清空，否则会一直追加，并且应该放在添加表头之前
        self.table_view2_model.clear()

        # 添加井口数据
        tmp_rows = [self.excel_sheet.base_row] + self.excel_sheet.rows

        # 添加表头
        cnt = len(tmp_rows)
        labels = ['井口']
        labels.extend([str(x) for x in range(1, cnt + 1)])
        self.table_view2_model.setVerticalHeaderLabels(labels)
        for col, (unit, description) in enumerate(self.table_headers):
            header_item = QStandardItem(f'{unit}\n{description}')
            self.table_view2_model.setHorizontalHeaderItem(col, header_item)

        # 填充数据
        self.table_view2_model.blockSignals(True)
        for row_idx, row_data in enumerate(tmp_rows):
            for col_idx, cell_data in enumerate(row_data):
                if col_idx < 12:
                    item = QStandardItem()
                    item.setData(format(cell_data, '.2f'), Qt.DisplayRole)
                    item.setToolTip(str(cell_data))

                    # 设置文本对齐方式为右对齐，更适合数字显示
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    if 3 <= col_idx <= 11:
                        item.setEditable(False)
                        item.setBackground(QBrush(QColor(255, 255, 153)))
                    else:
                        item.setEditable(True)
                    if row_idx == 0:
                        item.setEditable(False)
                        item.setBackground(QBrush(QColor(237, 237, 237)))
                    self.table_view2_model.setItem(row_idx, col_idx, item)

        # 填充最后一行
        for col_idx in range(3, 12):
            item = QStandardItem()
            item.setData('', Qt.DisplayRole)
            item.setEditable(False)
            item.setBackground(QBrush(QColor(255, 255, 153)))
            self.table_view2_model.setItem(len(tmp_rows), col_idx, item)
        self.table_view2_model.blockSignals(False)
        # todo 这里不延迟会内存移除，程序崩溃退出，本机测试延迟1毫秒可以解决
        QTimer.singleShot(5, partial(self.tableView2.verticalScrollBar().setValue, self.vertical_scroll_pos))

        # 在第二个表格更新完成后，再更新第一个表格
        self.init_table1()

    def init_actions(self):
        # 复制
        self.action_copy.setShortcut(QKeySequence.Copy)
        self.action_copy.triggered.connect(self.slot_copy_selection)
        self.tableView2.addAction(self.action_copy)

        # 粘贴
        self.action_paste.setShortcut(QKeySequence.Paste)
        self.action_paste.triggered.connect(self.slot_paste_selection)
        self.tableView2.addAction(self.action_paste)

        # 删除选中内容动作
        self.action_delete.setShortcut(QKeySequence(Qt.ShiftModifier | Qt.Key_Delete))
        self.action_delete.triggered.connect(self.slot_delete_selection)
        self.tableView2.addAction(self.action_delete)

        # 删除选中的行
        self.action_del_row.setShortcut(QKeySequence.Delete)
        self.action_del_row.triggered.connect(self.slot_del_row_selection)
        self.tableView2.addAction(self.action_del_row)

    def show_context_menu(self, pos):
        menu = QMenu(self)
        menu.addAction(self.action_copy)
        menu.addAction(self.action_paste)
        menu.addSeparator()
        menu.addAction(self.action_delete)
        menu.addAction(self.action_del_row)
        menu.exec_(self.tableView2.viewport().mapToGlobal(pos))

    def save_settings(self):
        settings = {
            'orientation': self.excel_sheet.orientation,
            'ground_x': self.excel_sheet.ground_x,
            'ground_y': self.excel_sheet.ground_y,
            'well_depth1': self.well_depth1,
            'well_depth2': self.well_depth2,
            'tilt_angle': self.tilt_angle
        }
        with open('data/data.json', 'w', encoding='utf-8') as f:
            json.dump(settings, f, ensure_ascii=False, indent=4)

    def load_settings_and_init_line_edit(self) -> dict:
        data: dict = {}
        try:
            with open(self.file_json_path, 'r', encoding='utf-8') as f:
                loaded_data = json.load(f)
                if isinstance(loaded_data, dict):
                    data = loaded_data
        except Exception as e:
            print(e)

        orientation = str(data.get('orientation', '0'))
        ground_x = str(data.get('ground_x', '0'))
        ground_y = str(data.get('ground_y', '0'))
        tilt_angle = str(data.get('tilt_angle', '0'))

        self.lineEditOrientation.setText(orientation)
        self.lineEditX.setText(ground_x)
        self.lineEditY.setText(ground_y)
        self.lineEditTiltAngle.setText(tilt_angle)

        return data

    def init_file_watcher(self):
        self.watcher_csv.addPath(self.file_csv_path)
        self.watcher_csv.fileChanged.connect(self.slot_watcher_csv)
        self.watcher_json.addPath(self.file_json_path)
        self.watcher_json.fileChanged.connect(self.slot_watcher_json)

    def slot_watcher_csv(self):
        self.csv_to_excel_sheet()
        self.init_table2()

    def slot_watcher_json(self):
        self.load_settings_and_init_line_edit()

    def calc_true_thickness(self, index: int):
        well1 = self.well_depth1
        well2 = self.well_depth2
        angle = self.tilt_angle

        rs = -1
        if index == 0:
            rs = self.excel_sheet.calc_true_thickness_tilt_up_cut_down_to_tilt_up_cut_up(well1, well2, angle)
        elif index == 1:
            rs = self.excel_sheet.calc_true_thickness_tilt_down_cut_down_to_tilt_down_cut_up(well1, well2, angle)
        self.lineEditTrueThickness.setText(str(rs))
